package com.afdor.rws.event;

import com.afdor.rws.core.extension.ExtensionLoader;
import com.afdor.rws.core.strategy.IdGeneratorStrategy;
import com.afdor.rws.core.strategy.TimestampStrategy;
import com.afdor.rws.event.config.EventConfigUtils;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Objects;

/**
 * 事件对象
 *
 * @author 悭梵
 * @date Created in 2018-08-03 16:48
 */
@Setter
@Getter
@ToString
@EqualsAndHashCode
public class Event<T> implements Serializable, Cloneable {
    /**
     * 事件路径分隔符
     */
    public static final String EVENT_PATH_SPLIT = "/";

    /**
     * 事件唯一标识
     */
    @NonNull
    private String id;
    /**
     * 事件类型
     */
    @NonNull
    private Type type;
    /**
     * 事件执行状态
     */
    @NonNull
    private Status status;
    /**
     * 上级事件标识
     */
    private String parentId;
    /**
     * 当前事件路径
     */
    private String path;
    /**
     * 事件源/队列名称
     *
     * @return
     */
    @NonNull
    protected String eventSourceName;
    /**
     * 事件源
     */
    @NonNull
    protected T source;
    /**
     * 事件执行状态时间
     */
    @NonNull
    private long statusTime;
    /**
     * 事件发生时间
     */
    @NonNull
    private long occurrenceTime;
    /**
     * 重试次数
     * <li>注意该数据未持久化。</li>
     * <li>在各状态的执行次数。即：状态修改时，重置重试次数。</li>
     */
    private int retryCount = 0;

    protected Event() {

    }

    /**
     * 派生一个新事件
     *
     * @param parentEvent
     */
    Event(@NonNull Event parentEvent) {
        this();
        this.parentId = parentEvent.getId();
        this.path = parentEvent.getPath() + EVENT_PATH_SPLIT + this.id;
    }

    /**
     * 是否根事件
     *
     * @return
     */
    public boolean isRootEvent() {
        return StringUtils.isBlank(parentId);
    }

    public void setType(@NonNull Type type) {
        this.type = type;
    }

    public final void setStatus(@NonNull Status status) {
        if (!Objects.equals(this.status, status)) {
            // 状态修改时，重置重试次数
            this.retryCount = 0;
            this.status = status;
            this.statusTime = getTimestamp();
        }
    }

    /**
     * 多线程调用安全性问题由调用发实现
     */
    public void addRetry() {
        this.retryCount++;
    }

    /**
     * 持久化事件操作
     *
     * @return
     */
    public boolean persistence() {
        ExtensionLoader
                .getInstance(EventBus.class)
                .getExtensionOrDefault(EventConfigUtils.getProperty(EventConfigUtils.EVENT_EVENT_BUS_NAME))
                .getEventPersistenceManager()
                .addEvent(this);
        return true;
    }

    private String getEventId() {
        ExtensionLoader<IdGeneratorStrategy> idGeneratorStrategyExtensionLoader = ExtensionLoader.getInstance(IdGeneratorStrategy.class);
        // 获取配置的Id生成策略
        return String.valueOf(idGeneratorStrategyExtensionLoader.getExtensionOrDefault(EventConfigUtils.getProperty(EventConfigUtils.EVENT_STRATEGY_NAME_ID)).generate());
    }

    private long getTimestamp() {
        ExtensionLoader<TimestampStrategy> timestampStrategyExtensionLoader = ExtensionLoader.getInstance(TimestampStrategy.class);
        //  获取配置的时间获取
        return timestampStrategyExtensionLoader.getExtensionOrDefault(EventConfigUtils.getProperty(EventConfigUtils.EVENT_STRATEGY_NAME_TIMESTAMP)).get();
    }

    private void init() {
        this.id = getEventId();

        this.parentId = null;
        this.status = Status.NEW;
        this.statusTime = getTimestamp();
        this.occurrenceTime = this.statusTime;
        this.path = EVENT_PATH_SPLIT + this.id;
    }

    @ToString
    public static class EventBuilder<T extends Serializable> {
        /**
         * 事件源/队列名称
         *
         * @return
         */
        @NonNull
        private String eventSourceName;
        /**
         * 事件源
         */
        @NonNull
        private T source;
        /**
         * 事件类型
         */
        private Event.Type type;
        /**
         * 上级事件
         */
        private Event parentEvent;

        public EventBuilder() {
        }

        public Event.EventBuilder<T> eventSourceName(@NonNull String eventSourceName) {
            this.eventSourceName = eventSourceName;
            return this;
        }

        public Event.EventBuilder<T> source(@NonNull T source) {
            this.source = source;
            return this;
        }

        /**
         * 生成Type.BROADCAST事件对象
         *
         * @return
         */
        public Event<T> buildBroadcast() {
            this.type = Type.BROADCAST;
            return build();
        }

        /**
         * 生成Type.BROADCAST事件对象
         *
         * @return
         */
        public Event<T> buildSubEvent(@NonNull Event event) {
            this.type = Type.SUB_EVENT;
            this.parentEvent = event;
            return build();
        }

        /**
         * 生成事件对象
         *
         * @return
         */
        public Event<T> build() {
            if (StringUtils.isBlank(eventSourceName)) {
                throw new NullPointerException("事件源/队列名称不能为空");
            }
            Event event = null;
            if (parentEvent == null) {
                Event.Type $Type = this.type == null ? Type.EVENT : this.type;
                if (Objects.equals(Type.AGGREGATION_ROOT, $Type)) {
                    throw new IllegalStateException("请使用AggregationEvent.EventBuilder构建");
                } else {
                    event = new Event();
                    event.setType($Type);
                }
            } else {
                event = new Event(parentEvent);
                if (type == null) {
                    event.setType(Type.SUB_EVENT);
                } else {
                    event.setType(this.type);
                }
            }
            event.source = this.source;
            event.eventSourceName = this.eventSourceName;
            event.init();
            return event;
        }
    }

    /**
     * 事件类型
     */
    public enum Type {
        /**
         * 聚合根事件
         */
        AGGREGATION_ROOT("DEFAULT_EVENT_AGGREGATION_ROOT"),
        /**
         * 广播
         */
        BROADCAST("DEFAULT_EVENT_BROADCAST"),
        /**
         * 异步事件消息
         */
        EVENT(null),
        /**
         * 子事件/派生事件消息
         */
        SUB_EVENT(null);

        private String defaultSourceName;

        Type(String defaultSourceName) {
            this.defaultSourceName = defaultSourceName;
        }

        public String getDefaultSourceName() {
            return defaultSourceName;
        }

        @Override
        public String toString() {
            return this.name();
        }
    }

    /**
     * 事件状态枚举
     */
    public enum Status {
        /**
         * 新建
         */
        NEW,
        /**
         * 执行中，未返回结果
         */
        EXECUTION,
        /**
         * 手动执行中，未返回结果
         */
        MANUAL_EXECUTION,
        /**
         * 执行成功
         */
        SUCCEED,
        /**
         * 执行失败
         */
        FAILURE,
        /**
         * 执行失败，终止自动执行
         */
        FAILURE_END,
        /**
         * 手动执行失败
         */
        MANUAL_FAILURE,
        /**
         * 手动放弃执行
         */
        MANUAL_DISCARD
    }

}
